package robocup.component.actions;

import robocup.component.ControllerNumbers.PLAY_MODE;
import robocup.component.geometry.Vektor;

/**
 * This is a generic action, which is used by the trainer to set-up specific
 * situations by placing the players or the ball somewhere on the field, or to
 * change the play-mode.
 */
public class CoachAction
    extends Action {

  /**
   * The play-modes string representation.
   */
  private String  playMode;

  /**
   * The tricot number of the player, which should be replaced.
   */
  private int     playerNumber;

  /**
   * The teamname of the player, which should be replaced.
   */
  private String  team;

  /**
   * The x-coordinate of the new players position.
   */
  private double  xPos;

  /**
   * The y-coordinate of the new players position.
   */
  private double  yPos;

  /**
   * The players new facing direction.
   */
  private double  facingDir;

  /**
   * The new x-directed velocity of the player.
   */
  private double  xVel;

  /**
   * The new y-directed velocity of the player.
   */
  private double  yVel;

  /**
   * Is eye or ear t set on.
   */
  private boolean isOn = false;

  /**
   * <code>CoachAction</code> constructor for MOVE_PLAYER. This is used to
   * move a player to a certain position with a defined speed and body
   * direction.
   * 
   * @param pNumber
   *          The player's number.
   * @param team
   *          The team name.
   * @param xPos
   *          The position's x coordinate.
   * @param yPos
   *          The position's y coordinate.
   * @param facingDir
   *          The player's facing direction.
   * @param xVel
   *          The x-component of the player's velocity.
   * @param yVel
   *          The y-component of the player's velocity.
   */
  public CoachAction(
      final int pNumber,
      final String team,
      final double xPos,
      final double yPos,
      final double facingDir,
      final double xVel,
      final double yVel) {

    this._type = TYPE.COACH_MOVE_PLAYER;
    this.playerNumber = pNumber;
    this.team = team;
    Vektor pos = new Vektor(Vektor.XY, xPos, yPos);
    pos.rotate(90);
    this.xPos = pos.x;
    this.yPos = -pos.y;
    pos = new Vektor(Vektor.XY, xVel, yVel);
    pos.rotate(90);
    this.xVel = pos.x;
    this.yVel = -pos.y;
    this.facingDir = facingDir;
  }

  /**
   * <code>CoachAction</code> constructor for MOVE_BALL, moves the ball to a
   * certain position with defined speed.
   * 
   * @param xPos
   * @param yPos
   * @param xVel
   * @param yVel
   */
  public CoachAction(
      final double xPos,
      final double yPos,
      final double xVel,
      final double yVel) {

    this._type = TYPE.COACH_MOVE_BALL;
    Vektor pos = new Vektor(Vektor.XY, xPos, yPos);
    pos.rotate(90);
    this.xPos = pos.x;
    this.yPos = -pos.y;
    pos = new Vektor(Vektor.XY, xVel, yVel);
    pos.rotate(90);
    this.xVel = pos.x;
    this.yVel = -pos.y;
  }

  /**
   * <code>CoachAction</code> constructor, valid types are either
   * COACH_RECOVER, COACK_LOOK or COACH_START. Al these have no further
   * parameters, hence the type defines it exactly.
   * 
   * @param type -
   *          the action type (Action.TYPE)
   */
  public CoachAction(
      final TYPE type) {

    switch (type) {
      case COACH_RECOVER: {
        this._type = TYPE.COACH_RECOVER;
        break;
      }
      case COACH_LOOK: {
        this._type = TYPE.COACH_LOOK;
        break;
      }
      default: {
        this._type = TYPE.COACH_START;
        break;
      }
    }
  }

  /**
   * <code>CoachAction</code> constructor, is used to set the eye or ear on.
   * Note that this is already done in the initProtocol of the coach/trainer.
   * 
   * @param type -
   *          either COACH_EYE_ON or COACH_EAR_MODE
   * @param on -
   *          if on == true, then the ear/eye is set on.
   */
  public CoachAction(
      final TYPE type,
      final boolean on) {

    this.isOn = on;
    switch (type) {
      case COACH_EYE_MODE: {
        this._type = TYPE.COACH_EYE_MODE;
        break;
      }
      default: {
        this._type = TYPE.COACH_EAR_MODE;
        break;
      }
    }
  }

  /**
   * <code>CoachAction</code> constructor - sets a new play-mode defined by
   * the given string.
   * 
   * @param playMode -
   *          the string representation of the playmode in valid syntax.
   */
  public CoachAction(
      final String playMode) {

    this._type = TYPE.COACH_PLAY_MODE;
    this.playMode = playMode;
  }

  /**
   * <code>toString</code>, creates the string representation of this action.
   * Due to the different types, different actions may result.
   * 
   * <ul>
   * <li>COACH_EYE_MODE : (eye &lt;on|off&gt;)
   * <li>COACH_EAR_MODE : (ear &lt;on|off&gt;)
   * <li>COACH_PLAY_MODE : (change_mode &lt;playMode&gt;)
   * <li>COACH_START : (start)
   * <li>COACH_RECOVER : (recover)
   * <li>COACH_LOOK : (look)
   * <li>COACH_MOVE_BALL : (move (ball) &lt;xPos&gt; &lt;yPos&gt; 0.0
   * &lt;xVel&gt; &lt;yVel&gt;)
   * <li>COACH_MOVE_PLAYER : (move (player &lt;team&gt; &lt;playerNumber&gt;)
   * &lt;Pos&gt; &lt;yPos&gt; &lt;facingDir&gt; &lt;xVel&gt; &lt;yVel&gt;)
   * <li>COACH_KILL_PLAYER : (kill_player &lt;side&gt; &lt;num&gt;) !Note! -
   * this command is not understood in the usual server version. Use the
   * extended version of team dainamite instead!
   * </ul>
   */
  @Override
  public String toString() {

    String cmd = new String();
    switch (this._type) {
      case COACH_EYE_MODE:
        cmd = new String("(eye " + (this.isOn ? "on" : "off") + ")");
        break;
      case COACH_EAR_MODE:
        cmd = new String("(ear " + (this.isOn ? "on" : "off") + ")");
        break;
      case COACH_PLAY_MODE:
        cmd = new String("(change_mode " + this.playMode + ")");
        break;
      case COACH_START:
        cmd = new String("(start)");
        break;
      case COACH_RECOVER:
        cmd = new String("(recover)");
        break;
      case COACH_LOOK:
        cmd = new String("(look)");
        break;
      case COACH_MOVE_BALL:
        cmd = new String(
            "(move (ball) " + this.xPos + " " + this.yPos + " 0.0 " + this.xVel + " " + this.yVel + ")");
        break;
      case COACH_MOVE_PLAYER:
        cmd = new String(
            "(move (player " + this.team + " " + this.playerNumber + ") " + this.xPos + " " + this.yPos + " " + this.facingDir + " " + this.xVel + " " + this.yVel + ")");
        break;
      case COACH_KILL_PLAYER:
        cmd = this.playMode;
      default:
        break;
    }
    return cmd;
  }

  /**
   * Constant string representation for play-mode: before_kick_off
   */
  public static final String PLAY_MODE_BEFORE_KICK_OFF     = new String(
                                                               "before_kick_off");

  /**
   * Constant string representation for play-mode: time_over
   */
  public static final String PLAY_MODE_TIME_OVER           = new String(
                                                               "time_over");

  /**
   * Constant string representation for play-mode: play_on
   */
  public static final String PLAY_MODE_PLAY_ON             = new String(
                                                               "play_on");

  /**
   * Constant string representation for play-mode: kick_off_left
   */
  public static final String PLAY_MODE_KICK_OFF_LEFT       = new String(
                                                               "kick_off_left");

  /**
   * Constant string representation for play-mode: kick_off_right
   */
  public static final String PLAY_MODE_KICK_OFF_RIGHT      = new String(
                                                               "kick_off_right");

  /**
   * Constant string representation for play-mode: kick_in_left
   */
  public static final String PLAY_MODE_KICK_IN_LEFT        = new String(
                                                               "kick_in_left");

  /**
   * Constant string representation for play-mode: kick_in_right
   */
  public static final String PLAY_MODE_KICK_IN_RIGHT       = new String(
                                                               "kick_in_right");

  /**
   * Constant string representation for play-mode: free_kick_left
   */
  public static final String PLAY_MODE_FREE_KICK_LEFT      = new String(
                                                               "free_kick_left");

  /**
   * Constant string representation for play-mode: free_kick_right
   */
  public static final String PLAY_MODE_FREE_KICK_RIGHT     = new String(
                                                               "free_kick_right");

  /**
   * Constant string representation for play-mode: corner_kick_left
   */
  public static final String PLAY_MODE_CORNER_KICK_LEFT    = new String(
                                                               "corner_kick_left");

  /**
   * Constant string representation for play-mode: corner_kick_right
   */
  public static final String PLAY_MODE_CORNER_KICK_RIGHT   = new String(
                                                               "corner_kick_right");

  /**
   * Constant string representation for play-mode: goal_kick_left
   */
  public static final String PLAY_MODE_GOAL_KICK_LEFT      = new String(
                                                               "goal_kick_left");

  /**
   * Constant string representation for play-mode: goal_kick_right
   */
  public static final String PLAY_MODE_GOAL_KICK_RIGHT     = new String(
                                                               "goal_kick_right");

  /**
   * Constant string representation for play-mode: goal_left
   */
  public static final String PLAY_MODE_GOAL_LEFT           = new String(
                                                               "goal_left");

  /**
   * Constant string representation for play-mode: goal_right
   */
  public static final String PLAY_MODE_GOAL_RIGHT          = new String(
                                                               "goal_right");

  /**
   * Constant string representation for play-mode: penalty_setup_left
   */
  public static final String PLAY_MODE_PENALTY_SETUP_LEFT  = new String(
                                                               "penalty_setup_left");

  /**
   * Constant string representation for play-mode: penalty_setup_right
   */
  public static final String PLAY_MODE_PENALTY_SETUP_RIGHT = new String(
                                                               "penalty_setup_right");

  /**
   * Constant string representation for play-mode: penalty_ready_left
   */
  public static final String PLAY_MODE_PENALTY_READY_LEFT  = new String(
                                                               "penalty_ready_left");

  /**
   * Constant string representation for play-mode: penalty_ready_right
   */
  public static final String PLAY_MODE_PENALTY_READY_RIGHT = new String(
                                                               "penalty_ready_right");

  /**
   * Constant string representation for play-mode: penalty_taken_left
   */
  public static final String PLAY_MODE_PENALTY_TAKEN_LEFT  = new String(
                                                               "penalty_taken_left");

  /**
   * Constant string representation for play-mode: penalty_taken_right
   */
  public static final String PLAY_MODE_PENALTY_TAKEN_RIGHT = new String(
                                                               "penalty_taken_right");

  /**
   * Constant string representation for play-mode: penalty_miss_left
   */
  public static final String PLAY_MODE_PENALTY_MISS_LEFT   = new String(
                                                               "penalty_miss_left");

  /**
   * Constant string representation for play-mode: penalty_miss_right
   */
  public static final String PLAY_MODE_PENALTY_MISS_RIGHT  = new String(
                                                               "penalty_miss_right");

  /**
   * Constant string representation for play-mode: penalty_score_left
   */
  public static final String PLAY_MODE_PENALTY_SCORE_LEFT  = new String(
                                                               "penalty_score_left");

  /**
   * Constant string representation for play-mode: penalty_score_right
   */
  public static final String PLAY_MODE_PENALTY_SCORE_RIGHT = new String(
                                                               "penalty_score_right");

  /**
   * <code>getPlayMode</code> - retrieves the play-mode string representation,
   * given the playmode constant from ControllerNumbers.PLAY_MODE.
   * 
   * @param mode -
   *          a play-mode enum constant
   * @return String - the play mode as valid string
   */
  public static final String getPlayMode(final PLAY_MODE mode) {

    switch (mode) {
      case BEFORE_KICK_OFF: {
        return CoachAction.PLAY_MODE_BEFORE_KICK_OFF;
      }
      case CORNER_KICK_OTHER: {
        return CoachAction.PLAY_MODE_BEFORE_KICK_OFF;
      }
      case CORNER_KICK_OWN: {
        return CoachAction.PLAY_MODE_BEFORE_KICK_OFF;
      }
      case FREE_KICK_OTHER: {
        return CoachAction.PLAY_MODE_FREE_KICK_RIGHT;
      }
      case GOAL_KICK_OWN: {
        return CoachAction.PLAY_MODE_FREE_KICK_LEFT;
      }
      case GOAL_OTHER: {
        return CoachAction.PLAY_MODE_GOAL_RIGHT;
      }
      case GOAL_OWN: {
        return CoachAction.PLAY_MODE_GOAL_LEFT;
      }
      case KICK_IN_OTHER: {
        return CoachAction.PLAY_MODE_KICK_IN_RIGHT;
      }
      case KICK_IN_OWN: {
        return CoachAction.PLAY_MODE_KICK_IN_LEFT;
      }
      case KICK_OFF_OTHER: {
        return CoachAction.PLAY_MODE_KICK_OFF_RIGHT;
      }
      case KICK_OFF_OWN: {
        return CoachAction.PLAY_MODE_KICK_OFF_LEFT;
      }
      case PENALTY_MISS_OTHER: {
        return CoachAction.PLAY_MODE_PENALTY_MISS_RIGHT;
      }
      case PENALTY_MISS_OWN: {
        return CoachAction.PLAY_MODE_PENALTY_MISS_LEFT;
      }
      case PENALTY_READY_OTHER: {
        return CoachAction.PLAY_MODE_PENALTY_READY_RIGHT;
      }
      case PENALTY_READY_OWN: {
        return CoachAction.PLAY_MODE_PENALTY_READY_LEFT;
      }
      case PENALTY_SCORE_OTHER: {
        return CoachAction.PLAY_MODE_PENALTY_SCORE_RIGHT;
      }
      case PENALTY_SCORE_OWN: {
        return CoachAction.PLAY_MODE_PENALTY_SCORE_LEFT;
      }
      case PENALTY_SETUP_OTHER: {
        return CoachAction.PLAY_MODE_PENALTY_SETUP_RIGHT;
      }
      case PENALTY_SETUP_OWN: {
        return CoachAction.PLAY_MODE_PENALTY_SETUP_LEFT;
      }
      case PENALTY_TAKEN_OTHER: {
        return CoachAction.PLAY_MODE_PENALTY_TAKEN_RIGHT;
      }
      case PENALTY_TAKEN_OWN: {
        return CoachAction.PLAY_MODE_PENALTY_TAKEN_LEFT;
      }
      case PLAY_ON: {
        return CoachAction.PLAY_MODE_PLAY_ON;
      }
      case TIME_OVER: {
        return CoachAction.PLAY_MODE_TIME_OVER;
      }
      default: {
        return null;
      }
    }
  }

  /**
   * This method changes the Position of this player/ball, depending on the
   * action type.
   * 
   * @param x -
   *          the x position
   * @param y -
   *          the y position
   */
  public void setPosition(final double x,
      final double y) {

    this.xPos = x;
    this.yPos = y;
  }

  /**
   * This method changes the speed of this player/ball, depending on the action
   * type.
   * 
   * @param x -
   *          the x velocity
   * @param y -
   *          the y velocity
   */
  public void setSpeed(final double d,
      final double e) {

    this.xVel = d;
    this.yVel = e;
  }

  /**
   * Sets the unum of the player, which should be moved.
   * 
   * @param unum -
   *          a players tricot number
   */
  public void setUnum(final int unum) {

    this.playerNumber = unum;
  }

  /**
   * Sets the teamname of a player, which should be moved.
   * 
   * @param teamName -
   *          the name of the team
   */
  public void setTeamName(final String teamName) {

    this.team = teamName;
  }

  /**
   * Returns the actual defined team-name.
   * 
   * @return String - the name of the team, from which a player should be moved.
   */
  public String getTeam() {

    return this.team;
  }

  /**
   * Sets the body direction for a player, which should be moved.
   * 
   * @param bodyDir -
   *          the new body direction.
   */
  public void setBodyDir(final double bodyDir) {

    this.facingDir = bodyDir;
  }

  /**
   * Sets the type of the action. Use only
   * <ul>
   * <li>COACH_EYE_MODE
   * <li>COACH_EAR_MODE
   * <li>COACH_PLAY_MODE
   * <li>COACH_START
   * <li>COACH_RECOVER
   * <li>COACH_LOOK
   * <li>COACH_MOVE_BALL
   * <li>COACH_MOVE_PLAYER
   * <li>COACH_KILL_PLAYER
   * </ul>
   * Other s are not defined here.
   * 
   * @param type -
   *          the actions type.
   */
  public void setType(final TYPE type) {

    this._type = type;
  }

  /**
   * Clones the current action, such that an exact copy is created and returned.
   * 
   * @param d -
   *          an angle, which is added to the facing-direction
   * @return the new action, which is equal to this one, except the facing
   *         direction, which could be altered by the parameter
   */
  public CoachAction cloned(final double d) {

    final CoachAction action = new CoachAction("");
    action._type = this._type;
    action.playerNumber = this.playerNumber;
    action.team = this.team;

    action.xPos = this.xPos;
    action.yPos = this.yPos;

    action.xVel = this.xVel;
    action.yVel = this.yVel;
    action.facingDir = Vektor.normalize(this.facingDir + d);
    return action;
  }

  /**
   * This method returns a copy of the position of the command. This is only
   * defined, if this is a player-move command. The returned position is of
   * Robocup-Agent WM coordinate space.
   * 
   * @return - the position
   */
  public Vektor getPosition() {

    final Vektor pos = new Vektor(Vektor.XY, this.xPos, -this.yPos);
    pos.rotate(-90);
    return pos;
  }

  /**
   * This method returns a copy of the speed of the command. This is only
   * defined, if this is a player-move command. The returned speed is of
   * Robocup-Agent WM coordinate space.
   * 
   * @return - the speed
   */
  public Vektor getSpeed() {

    final Vektor spd = new Vektor(Vektor.XY, this.xVel, -this.yVel);
    spd.rotate(-90);
    return spd;
  }

  /**
   * This method retrieves the uNumber of the player. This is only defined, if
   * this is a player-move command.
   * 
   * @return the unum
   */
  public int getNumber() {

    return this.playerNumber;
  }
}
